#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

#define NUM 1024
#define OPT_NUM 64

#define NONE_REDIR   0
#define INPUT_REDIR  1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

#define trimSpace(start)  do{\
                          while(isspace(*start))\
                              ++start;\
                          }while(0)

int RedirType = NONE_REDIR;
char *redirFile = NULL;

char lineCommand[NUM];
char *myargv[OPT_NUM];
int lastCode = 0;
int lastSig = 0;

void commandCheck(char* commands)
{
  assert(commands);
  char *start = commands;
  char *end = commands+strlen(commands);

  while(start<end)
  {
    // 追加/输出
    if(*start == '>')
    {
      *start = '\0';
      start++;
      if(*start == '>')
      {
        // "ls -a >> file.txt"
        RedirType = APPEND_REDIR;
        start++;
      }
      else 
      {
        // "ls -a > file.txt"
        RedirType = OUTPUT_REDIR;
      }
      trimSpace(start);
      redirFile = start;
      break;
    }
    // 输入
    else if(*start == '<') 
    {
      // " cat <      file.txt"
      *start = '\0';
      start++;
      trimSpace(start);
      // 填写重定向信息
      RedirType = INPUT_REDIR;
      redirFile = start;
      break;
    }
    else 
    {
      start++;
    }
  }
}

int main()
{
  while(1){
    // 每次循环前将重定向信息设置为NULL 错误信息也置为0
    RedirType = NONE_REDIR;
    redirFile = NULL;
    errno = 0;
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);
  
    // 获取用户输入
    char* s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
    assert(s != NULL);
    (void)s;
    // 清除最后一个\n
    lineCommand[strlen(lineCommand)-1] = 0;
    // printf("test:%s\n",lineCommand);
  

    // 重定向判断
    commandCheck(lineCommand);

    // 字符串切割
    myargv[0] = strtok(lineCommand," ");
    int i = 1;
    if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
    {
      myargv[i++] = (char*)"--color=auto";
    }

    while(myargv[i++] = strtok(NULL," "));

    if(myargv[0] != NULL && strcmp(myargv[0],"cd")==0)
    {
      if(myargv[1] != NULL)
      {
        chdir(myargv[1]);
      }
      continue;
    }
    if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0],"echo")==0)
    {
      if(strcmp(myargv[1],"$?")==0)
      {
        printf("%d,%d\n",lastCode,lastSig);
      }
      else 
      {
        printf("%s\n",myargv[1]);
      }
      continue;
    }


    //测试切割是否成功
#ifdef DEBUG 
    for(int i = 0; myargv[i];i++)
    {
      printf("myargv[%d]:%s\n",i,myargv[i]);
    }
#endif 

    // 执行命令
    pid_t id = fork();
    assert(id != -1);
  
    // 子进程
    if(id == 0)
    {
      switch(RedirType)
      {
        case NONE_REDIR:
          // 什么都不做
          break;
        case INPUT_REDIR:
          {
            int fd = open(redirFile,O_RDONLY);
            if(fd<0)
            {
              perror("open");
              exit(errno);
            }
            //重定向的文件成功打开
            // dup2进行重定向
            dup2(fd,0);
          }
          break;
        case OUTPUT_REDIR:
        case APPEND_REDIR:
          {
            //使得创建的文件按照自己的权限创建
            umask(0);
            int flags = O_WRONLY | O_CREAT;
            if(RedirType == APPEND_REDIR) flags |= O_APPEND;
            else flags |= O_TRUNC; 

            int fd = open(redirFile,flags,0666);
            if(fd < 0)
            {
              perror("open");
              exit(errno);
            }
            dup2(fd,1);
          }
          break;
        default:
          printf("bug?\n");
          break;
      }
      execvp(myargv[0],myargv);
      exit(100);
    }
    // 父进程
    int status = 0;
    pid_t ret = waitpid(id,&status,0);
    assert(ret>0);
    (void)ret;
    lastCode = ((status>>8) & 0xFF);
    lastSig = (status & 0x7F);
  }
  return 0;
}

